/**
 * 
 */
package jmine.tec.utils.collection.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import jmine.tec.utils.collection.DropTree;
import jmine.tec.utils.collection.Dropable;

/**
 * <i>DropTreeSet</i> é uma implementação de <i>DropTree</i> que mantém nos <i>pools</i> de seus nós um <i>Set</i>, não permitindo a
 * inserção de objetos repetidos.
 * 
 * @author gigante
 * @param <T> o parâmetro para dropable
 */
public class DropTreeSet<T> implements DropTree<T> {

    private DropNode<T> head;

    /**
     * @param node o nó a ser inserido
     */
    public void add(final Dropable<T> node) {
        if (this.head != null) {
            for (DropNode<T> dropNode : this.head.getNodes()) {
                if (dropNode.allowAndInsert(node)) {
                    return;
                }
            }
            this.head.addToSink(node);
        }
    }

    /**
     * @return a lista com todos os Set's não vazios de todos os nós.
     */
    public List<Collection<Dropable<T>>> asList() {
        List<Collection<Dropable<T>>> ret = new ArrayList<Collection<Dropable<T>>>();
        if (this.head != null) {
            this.head.setsAsList(ret);
        }
        return ret;
    }

    /**
     * @param node o nó a ser adicionado como "duto" da árvore
     */
    public void insertNode(final Dropable<T> node) {
        if (this.head == null) {
            this.head = new DropNode<T>(null);
        }
        this.head.insertNode(node);
    }

    /**
     * @return a lista com os "dutos" filhos de primeiro nível
     */
    public List<Dropable<T>> getHeadNodes() {
        if (this.head == null) {
            this.head = new DropNode<T>(null);
        }
        List<Dropable<T>> ret = new ArrayList<Dropable<T>>();
        for (DropNode<T> node : this.head.getNodes()) {
            ret.add(node.getComparator());
        }
        return ret;
    }

    /**
     * @param d o nó para recuperar os filhos
     * @return a lista com os nós filhos
     */
    public List<Dropable<T>> getNodesFrom(final Dropable<T> d) {
        if (this.head != null) {
            return this.head.getNodesFrom(d);
        }
        return null;
    }

    /**
     * @param d o node
     * @return a lista com os valores em d
     */
    public List<Dropable<T>> getValuesOf(final Dropable<T> d) {
        if (this.head != null) {
            return this.head.getValuesOf(d);
        }
        return null;
    }

    /**
     * @return a lista com o resto dos elementos
     */
    public List<Dropable<T>> getSink() {
        if (this.head != null && this.head.getSink() != null) {
            return new ArrayList<Dropable<T>>(this.head.getSink());
        }
        return null;
    }

}
